package edu.xhu.behavior.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.xhu.api.id.IdClient;
import edu.xhu.api.user.UserClient;
import edu.xhu.behavior.config.IMClient;
import edu.xhu.behavior.mapper.BiliUserChatDetailMapper;
import edu.xhu.behavior.mapper.BiliUserChatMapper;
import edu.xhu.behavior.service.BiliUserChatService;
import edu.xhu.behavior.util.IdGen;
import edu.xhu.common.exception.CustomException;
import edu.xhu.model.behavior.dto.ChatDto;
import edu.xhu.model.behavior.dto.ChatLoginDto;
import edu.xhu.model.behavior.dto.PrivateMessageDto;
import edu.xhu.model.behavior.enums.MessageStatus;
import edu.xhu.model.behavior.pojos.BiliUserChat;
import edu.xhu.model.behavior.pojos.BiliUserChatDetail;
import edu.xhu.model.behavior.vo.ChatVo;
import edu.xhu.model.biliuser.pojos.BiliUser;
import edu.xhu.model.biliuser.vos.BiliUserConfigVo;
import edu.xhu.model.biliuser.vos.BiliUserInfoVo;
import edu.xhu.model.biliuser.vos.BiliUserVo;
import edu.xhu.model.common.constants.ChatConstants;
import edu.xhu.model.common.constants.kafka.topic.ChatTopic;
import edu.xhu.model.common.dtos.ResponseResult;
import edu.xhu.model.common.enums.AppHttpCodeEnum;
import edu.xhu.model.common.enums.IsFlagEnum;
import edu.xhu.model.im.constant.IMConstant;
import edu.xhu.model.im.constant.IMRedisKey;
import edu.xhu.model.im.enums.IMCmdType;
import edu.xhu.model.im.enums.IMTerminalType;
import edu.xhu.model.im.pojos.IMRecvInfo;
import edu.xhu.model.im.pojos.IMUserInfo;
import edu.xhu.utils.thread.BiliUserThreadLocalUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class BiliUserChatServiceImpl extends ServiceImpl<BiliUserChatMapper, BiliUserChat> implements BiliUserChatService {

    private final StringRedisTemplate redisTemplate;

    private final KafkaTemplate<String, String> kafkaTemplate;

    private final BiliUserChatDetailMapper biliUserChatDetailMapper;

    private final UserClient userClient;

    private final IdClient idClient;

    private final IMClient imClient;


    /**
     * 创建一个个人聊天:当前用户对toId发起一个新的个人聊天
     * 缓存里查,看有没有这个toId,有,不用创建了;无,创建(比打一次DB快)
     */
    @Override
    @Transactional
    public ResponseResult<?> createChat(Long toId) {
        //0.check
        BiliUser user = BiliUserThreadLocalUtil.getUser();
        if (toId == null || user == null || user.getUserId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ChatVo chatVo;         //返回值
        //0.1,看toId有没有
        Object o = redisTemplate.opsForHash().get(ChatConstants.USER_RECENT_CHAT_TOID.concat(user.getUserId().toString()), toId.toString());
        String flag = (String) o;
        //1.0,如果聊天创建过(因为有这个toId)
        if (flag != null) {
            //如果是被删除状态(聊天list没有,因为被删除了;toId有,但是为false)
            if (flag.equals(IsFlagEnum.NO.getCode())) {
                //DB查出来一个聊天信息,一个详情信息
                BiliUserChat dbChat = getOne(Wrappers.<BiliUserChat>lambdaQuery()
                        .eq(BiliUserChat::getUserId, user.getUserId())
                        .eq(BiliUserChat::getToId, toId)
                        .eq(BiliUserChat::getIsDelete, IsFlagEnum.YES.getCode()));
                //db也没有,ohNo,缓存脏了,新创建吧
                if (dbChat == null) {
                    try {
                        chatVo = doCreateChat(user.getUserId(), toId);
                    } catch (Exception e) {
                        log.error("创建聊天失败:{}", e.toString());
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  //标记为回滚
                        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
                    }
                } else {
                    //db有,以db为准
                    ChatVo dbChatVo = new ChatVo();
                    dbChatVo.setChat(dbChat);
                    //查详情(user,to,都没del,不是group,没有撤回,按time倒序排序,返回最近20条就行了)
                    List<BiliUserChatDetail> details = doQueryChatDetail(user.getUserId(), toId, 1, 20);
                    //最终一致性
                    if (details != null) {
                        dbChatVo.setDetail(details);
                    } else {
                        dbChatVo.setDetail(Collections.emptyList());
                    }
                    chatVo = dbChatVo;
                    //更新一下dbChat
                    dbChat.setLastTime(new Date());
                    dbChat.setIsDelete(IsFlagEnum.NO.getCode());
                    if (!updateById(dbChat)) {
                        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "更新chat表失败！");
                    }
                }
            }
            //否则,有toId的窗口,不用创建了
            else {
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS.getCode(), "处于聊天中");
            }
        }
        //2.0缓存里面没有toId
        else {
            ChatVo dtoWithNoCache = new ChatVo();
            //2.1再去查DB
            //DB查出来一个聊天信息,一个详情信息
            BiliUserChat dbChatWithoutCache = getOne(Wrappers.<BiliUserChat>lambdaQuery()
                    .eq(BiliUserChat::getUserId, user.getUserId())
                    .eq(BiliUserChat::getToId, toId));
            //如果db有
            if (dbChatWithoutCache != null) {
                //2.2如果是已删除状态,重新启用,修改状态
                if (dbChatWithoutCache.getIsDelete().equals(IsFlagEnum.YES.getCode())) {
                    dbChatWithoutCache.setLastTime(new Date());
                    dbChatWithoutCache.setIsDelete(IsFlagEnum.NO.getCode());
                    if (!updateById(dbChatWithoutCache)) {
                        return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "更新chat表失败！");
                    }
                }
                //2.3把聊天详情查出来
                //查详情(user,to,都没del,不是group,没有撤回,按time倒序排序,返回最近20条就行了)
                List<BiliUserChatDetail> details = doQueryChatDetail(user.getUserId(), toId, 1, 20);
                //最终一致性
                if (details != null) {
                    dtoWithNoCache.setDetail(details);
                } else {
                    dtoWithNoCache.setDetail(Collections.emptyList());
                }
                dtoWithNoCache.setChat(dbChatWithoutCache);
                chatVo = dtoWithNoCache;
            } else {
                //DB也没有,新创建了
                try {
                    chatVo = doCreateChat(user.getUserId(), toId);
                } catch (Exception e) {
                    log.error("创建聊天失败:{}", e.toString());
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  //标记为回滚
                    return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
                }
            }
        }
        //3.0,统一添加时间戳,添加user
        long time = System.currentTimeMillis();
        Object biliuserData = userClient.queryUserInfoById(toId).getData();
        //user
        BiliUserVo biliUserVo = new BiliUserVo();
        BeanUtil.copyProperties(biliuserData, biliUserVo);
        //userInfo
        BiliUserInfoVo biliUserInfoVo = new BiliUserInfoVo();
        BeanUtil.copyProperties(biliuserData, biliUserInfoVo);
        //userConfig
        BiliUserConfigVo biliUserConfigVo = new BiliUserConfigVo();
        BeanUtil.copyProperties(biliuserData, biliUserConfigVo);
        //Chat
        chatVo.setUser(biliUserVo);
        chatVo.setUserInfo(biliUserInfoVo);
        chatVo.setUserConfig(biliUserConfigVo);
        chatVo.setTimeStamp(time);
        //4.0,都ok了,把chatDto放到缓存(缓存的时候,不要缓存详情)
        ChatVo noDetailVo = new ChatVo();
        BeanUtils.copyProperties(chatVo, noDetailVo);
        noDetailVo.setDetail(Collections.emptyList());
        redisTemplate.opsForZSet().add(ChatConstants.USER_RECENT_CHAT_LIST.concat(user.getUserId().toString()), JSONUtil.toJsonStr(noDetailVo), time);
        //不管怎样,都刷新一下toId
        redisTemplate.opsForHash().put(ChatConstants.USER_RECENT_CHAT_TOID.concat(user.getUserId().toString()), toId.toString(), "1");
        return ResponseResult.okResult(chatVo);
    }

    /**
     * 发送个人私聊消息
     * 高并发接口,提高吞吐量,全权交给kafka管理,发个消息就走了;kafka那边异步批处理
     */
    @Override
    @Transactional
    public ResponseResult<?> sendMessage(PrivateMessageDto dto) {
        BiliUser user = BiliUserThreadLocalUtil.getUser();
        if (user == null || user.getUserId() == null || dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        BiliUserChatDetail detail;
        try {
            //0.离线标识
            String isOff = IsFlagEnum.NO.getCode();
            //1.判断toId是否在线,以及redis路由(这里redis路由形同虚设了,只是用来判断toId是否在线......)
            String sendKey = String.join(":", IMRedisKey.IM_USER_SERVER_ID
                    , dto.getToId().toString(), "0");   //路由的key,默认web端,0
            String serverId = redisTemplate.opsForValue().get(sendKey);
            //2.构建消息详情
            detail = buildDetail(user.getUserId(), dto);
            //3.封装发送的消息
            IMRecvInfo recvInfo = buildRecvInfo(user.getUserId(), dto.getToId(),
                    IMCmdType.PRIVATE_MESSAGE.code(), JSON.toJSONString(Collections.singletonList(detail)), detail.getDetailId(), true);
            //4.根据serverId,判断toId是否在线
            if (serverId != null) {
                //4.1 当前toId在线,kafka,发个消息就走了,发给对应serverId的topic
                kafkaTemplate.send(ChatTopic.BILL_CHAT_TOPIC, JSONUtil.toJsonStr(recvInfo));
                log.info("对主题:{},发送了消息:{}", ChatTopic.BILL_CHAT_TOPIC, recvInfo);
            } else {
                //4.2 离线处理,直接持久化到db(离线消息不能保存到redis,用户可能很久不上线！非常占内存！)
                isOff = IsFlagEnum.YES.getCode();
            }
            //5.设置detail的isOff
            detail.setIsOffline(isOff);
            //6.异步更新消息表未读数以及保存消息到db(kafka批处理,增加吞吐量,减少打db)
            kafkaTemplate.send(ChatTopic.BILL_CHAT_DETAIL_SAVE, JSONUtil.toJsonStr(detail));
            log.info("对主题:{},发送了消息:{}", ChatTopic.BILL_CHAT_DETAIL_SAVE, detail);
        } catch (Exception e) {
            log.error("保存,发送消息失败,错误信息:{}", e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  //标记为回滚
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        return ResponseResult.okResult(JSON.toJSONString(detail));
    }

    /**
     * 拉取toId离线信息(这里,toId是userId发给toId的,detail里应该拉取userId是toId的消息)
     * toId:发送离线消息的人
     * minId:发送的最后一条消息的id(如果为0,表示完全没有聊过天)
     */
    @Override
    public ResponseResult<?> pullOfflineMessage(Long toId, Long minId) {
        //0.准备
        BiliUser user = BiliUserThreadLocalUtil.getUser();
        if (user == null || user.getUserId() == null || toId == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //如果当前user不在线,拉取离线信息失败！
        if (!imClient.isOnline(user.getUserId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "网络连接失败,无法拉取离线消息");
        }
        //1.从db中拉取最近一个月的1000条离线
        List<BiliUserChatDetail> offlineDetails = getLastOneMonthLimit(user.getUserId(), toId, minId);
        List<BiliUserChatDetail> sortedDetails = offlineDetails.stream()
                .sorted(Comparator.comparing(BiliUserChatDetail::getTime))
                .collect(Collectors.toList());
        //2.校验拉取到的离线消息
        if (offlineDetails == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        if (offlineDetails.isEmpty()) {
            return ResponseResult.okResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.推送这些消息
        try {
            IMRecvInfo recvInfo = buildRecvInfo(toId, user.getUserId(),
                    IMCmdType.PRIVATE_MESSAGE.code(), JSONUtil.toJsonStr(sortedDetails), null, true);
            kafkaTemplate.send(ChatTopic.BILL_CHAT_TOPIC, JSONUtil.toJsonStr(recvInfo));
        } catch (Exception e) {
            log.error("推送离线消息失败,原因未知:{}", e.toString());
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS.getCode(), "离线消息拉取中~~");
    }

    /**
     * 将会话中接收的消息状态置为已读
     * 测试:toId是274227962746765312,把5312发给0608的消息置为已读
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public ResponseResult<?> readedMessage(Long toId) {
        //0.准备并校验
//        BiliUser user = BiliUserThreadLocalUtil.getUser();
//        if (user == null || user.getUserId() == null || toId == null || user.getUserId().equals(toId)) {
//            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
//        }
        BiliUser user = new BiliUser();
        user.setUserId(2287248979079168L);
        //0.1把未读的消息查出来(toId->userId)
        List<BiliUserChatDetail> unReadDetails = biliUserChatDetailMapper.selectList(Wrappers.<BiliUserChatDetail>lambdaQuery()
                .eq(BiliUserChatDetail::getUserId, toId)        //toId发的
                .eq(BiliUserChatDetail::getToId, user.getUserId())  //我收的
                .eq(BiliUserChatDetail::getToDel, IsFlagEnum.NO.getCode())  //没有被toId删
                .eq(BiliUserChatDetail::getUserDel, IsFlagEnum.NO.getCode()) //没有被我删
                .eq(BiliUserChatDetail::getIsOffline, IsFlagEnum.NO.getCode())//不离线
                .eq(BiliUserChatDetail::getDetailStatus, MessageStatus.SENDED.getCode()));  //已送达
        if (unReadDetails.isEmpty()) {
            return ResponseResult.okResult("没有需要清理的未读~");
        }
        //1.推送消息给自己,清空会话列表上的已读数量(需要回调,清空unRead)
        try {
            //这里把detailId赋值为toId,方便回调做判断
            IMRecvInfo selfInfo = buildRecvInfo(user.getUserId(), user.getUserId(),
                    IMCmdType.READED.code(), "全部都读了~已读是0~", toId, true);
            kafkaTemplate.send(ChatTopic.BILL_CHAT_TOPIC, JSON.toJSONString(selfInfo));
            log.info("发送消息:{}给主题:{}......", selfInfo, ChatTopic.BILL_CHAT_TOPIC);
        } catch (Exception e) {
            log.info("推送已读清空消息失败:{}", e.toString());
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        //2.推送消息给对方,更新已读状态(需要回调,修改消息状态)
        try {
            IMRecvInfo readedInfo = buildRecvInfo(user.getUserId(), toId,
                    IMCmdType.READED.code(), JSON.toJSONString(unReadDetails), null, true);
            kafkaTemplate.send(ChatTopic.BILL_CHAT_TOPIC, JSON.toJSONString(readedInfo));
            log.info("发送消息:{}给主题:{}......", readedInfo, ChatTopic.BILL_CHAT_TOPIC);
        } catch (Exception e) {
            log.info("推送已读消息失败:{}", e.toString());
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS.getCode(), "推送已读中~~");
    }

    /**
     * 从db中拉取最近一个月的1000条离线
     */
    private List<BiliUserChatDetail> getLastOneMonthLimit(Long userId, Long toId, Long minId) {
        Date minDate = DateUtils.addMonths(new Date(), -1);
        LambdaQueryWrapper<BiliUserChatDetail> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.gt(BiliUserChatDetail::getDetailId, minId)                     //大于起始消息id(比最后一条消息记录要大)
                .ge(BiliUserChatDetail::getTime, minDate)                           //近一个月
                .ne(BiliUserChatDetail::getUserDel, IsFlagEnum.YES.getCode())       //没有被我删掉
                .ne(BiliUserChatDetail::getIsGroup, IsFlagEnum.YES.getCode())       //不是群聊
                .eq(BiliUserChatDetail::getIsOffline, IsFlagEnum.YES.getCode())     //是离线消息
                .eq(BiliUserChatDetail::getUserId, toId)                            //是toId发给我的
                .eq(BiliUserChatDetail::getToId, userId)                            //是我接收的
                .orderByDesc(BiliUserChatDetail::getTime)                           //按时间,从小到大排序
                .last("limit 1000");                                         //限制1000条
        return biliUserChatDetailMapper.selectList(queryWrapper);
    }

    /**
     * 构建聊天详情
     */
    private BiliUserChatDetail buildDetail(Long userId, PrivateMessageDto dto) {
        BiliUserChatDetail detail = new BiliUserChatDetail();
        detail.setDetailId(IdGen.getBehaviorId(idClient))
                .setUserId(userId)
                .setToId(dto.getToId())
                .setUserDel(IsFlagEnum.NO.getCode())
                .setToDel(IsFlagEnum.NO.getCode())
                .setIsGroup(IsFlagEnum.NO.getCode())
                .setDetailStatus(MessageStatus.UNSEND.getCode())
                .setContent(dto.getContent())
                .setTime(new Date());
        return detail;
    }

    /**
     * 构建消息体
     */
    private IMRecvInfo buildRecvInfo(Long userId, Long toId, Integer cmdType, String content, Long detailId, boolean isResult) {
        //a-sender
        IMUserInfo sender = new IMUserInfo();
        sender.setId(userId).setTerminal(0);
        //b-receivers
        IMUserInfo receiver = new IMUserInfo();
        receiver.setId(toId).setTerminal(0);
        //c-IM接收消息体
        IMRecvInfo recvInfo = new IMRecvInfo();
        recvInfo.setCmd(cmdType)
                .setData(content)
                .setDetailId(detailId)
                .setSender(sender)
                .setReceivers(Collections.singletonList(receiver))
                .setSendResult(isResult)   //需要回调
                .setServiceName(IMTerminalType.WEB.name());
        return recvInfo;
    }

    /**
     * 查询聊天详情
     */
    private List<BiliUserChatDetail> doQueryChatDetail(Long userId, Long toId, int page, int size) {
        page = page > 0 ? page : 1;
        size = size > 0 ? size : 20;
        List<BiliUserChatDetail> details;
        details = biliUserChatDetailMapper.selectList(Wrappers.<BiliUserChatDetail>lambdaQuery()
                .and(wrap -> wrap.and(
                        wp -> wp.eq(BiliUserChatDetail::getUserId, userId)
                                .eq(BiliUserChatDetail::getToId, toId)
                ).or(wp -> wp.eq(BiliUserChatDetail::getToId, userId)
                        .eq(BiliUserChatDetail::getUserId, toId)))
                .eq(BiliUserChatDetail::getUserDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getToDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getIsGroup, IsFlagEnum.NO.getCode())
                .ne(BiliUserChatDetail::getDetailStatus, MessageStatus.RECALL.getCode())  //不要撤回
                .ne(BiliUserChatDetail::getDetailStatus, MessageStatus.UNSEND.getCode())  //不要未送达
                .orderByDesc(BiliUserChatDetail::getTime)
                .last("limit " + (page - 1) + "," + size));
        return details;
    }

    /**
     * 创建chatDto
     */
    private ChatVo doCreateChat(Long userId, Long toId) {
        //0.校验
        if (userId == null || toId == null || userId.equals(toId)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //1.创建,添加聊天表
        BiliUserChat newChat = new BiliUserChat();
        try {
            newChat.setChatId(IdGen.getBehaviorId(idClient))
                    .setUserId(userId)
                    .setToId(toId)
                    .setIsDelete(IsFlagEnum.NO.getCode())
                    .setIsGroup(IsFlagEnum.NO.getCode())
                    .setUnRead(0)
                    .setLastTime(new Date());
        } catch (Exception e) {
            log.error("创建聊天表失败:{}", e.toString());
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        if (!save(newChat)) {
            log.error("创建聊天表,详情表失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        ChatVo chatVo = new ChatVo();
        chatVo.setChat(newChat);
        chatVo.setDetail(Collections.emptyList()); //返回空列表
        return chatVo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public ResponseResult<?> updateWhisperOnline(Long toId) {
        Long userId = BiliUserThreadLocalUtil.getUser().getUserId();
        String sendKey = String.join(":", IMRedisKey.IM_USER_SERVER_ID
                , userId.toString(), "0");   //路由的key,默认web端,0
        redisTemplate.expire(sendKey, IMConstant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS);
        //已读
        return readedMessage(toId);
    }

    @Override
    public ResponseResult<?> getChatList(Integer offset) {
        Map<String, Object> res = new HashMap<>();
        BiliUser user = BiliUserThreadLocalUtil.getUser();
        if (user == null || user.getUserId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "当前用户不存在！");
        }
        Long userId = user.getUserId();
        List<ChatDto> dtoList;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .rangeWithScores(ChatConstants.USER_RECENT_CHAT_LIST.concat(userId.toString()), 0, -1);
        if (typedTuples != null && !typedTuples.isEmpty()) {
            // 检查是否还有更多
            if (offset + 10 < typedTuples.size()) {
                res.put("more", true);
            } else {
                res.put("more", false);
            }
            dtoList = typedTuples.stream().parallel().map(stringTypedTuple -> {
                String value = stringTypedTuple.getValue();
                ChatDto dto = JSONUtil.toBean(value, ChatDto.class);
//                List<BiliUserChatDetail> newDetails = doQueryChatDetail(dto.getChat().getUserId(), dto.getChat().getToId(), 1, 20);
                //详情给空,设置time
                dto.setDetail(Collections.emptyList());
                if (stringTypedTuple.getScore() != null) {
                    dto.setTimeStamp(stringTypedTuple.getScore().longValue());
                }
                return dto;
            }).collect(Collectors.toList());
            res.put("list", dtoList);
        }
        return ResponseResult.okResult(res);
    }

    @Override
    public ResponseResult<?> getHistoryDetail(Long toId, Long detailId) {
        BiliUser user = BiliUserThreadLocalUtil.getUser();
        if (user == null || user.getUserId() == null || toId == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "用户信息不存在！");
        }
        if(detailId.equals(0L)){
            return ResponseResult.okResult(doQueryChatDetailForLatestHistory(user.getUserId(), toId));
        }
        return ResponseResult.okResult(doQueryChatDetailForHistoryBydetailId(user.getUserId(), toId, detailId));
    }

    private List<BiliUserChatDetail> doQueryChatDetailForHistoryBydetailId(Long userId, Long toId, Long detailId) {
        List<BiliUserChatDetail> details;
        details = biliUserChatDetailMapper.selectList(Wrappers.<BiliUserChatDetail>lambdaQuery()
                .and(wrap -> wrap.and(
                        wp -> wp.eq(BiliUserChatDetail::getUserId, userId)
                                .eq(BiliUserChatDetail::getToId, toId)
                ).or(wp -> wp.eq(BiliUserChatDetail::getToId, userId)
                        .eq(BiliUserChatDetail::getUserId, toId)))
                .eq(BiliUserChatDetail::getUserDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getToDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getIsGroup, IsFlagEnum.NO.getCode())
                .ne(BiliUserChatDetail::getDetailStatus, MessageStatus.RECALL.getCode())  //不要撤回
                .lt(BiliUserChatDetail::getDetailId, detailId)      //小于detailId
                .orderByDesc(BiliUserChatDetail::getTime)
                .last("limit 20"));
        // 将查询结果按时间升序排序
        List<BiliUserChatDetail> sortedDetails = details.stream()
                .sorted(Comparator.comparing(BiliUserChatDetail::getTime))
                .collect(Collectors.toList());
        return sortedDetails;
    }

    private List<BiliUserChatDetail> doQueryChatDetailForLatestHistory(Long userId, Long toId) {
        List<BiliUserChatDetail> details;
        details = biliUserChatDetailMapper.selectList(Wrappers.<BiliUserChatDetail>lambdaQuery()
                .and(wrap -> wrap.and(
                        wp -> wp.eq(BiliUserChatDetail::getUserId, userId)
                                .eq(BiliUserChatDetail::getToId, toId)
                ).or(wp -> wp.eq(BiliUserChatDetail::getToId, userId)
                        .eq(BiliUserChatDetail::getUserId, toId)))
                .eq(BiliUserChatDetail::getUserDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getToDel, IsFlagEnum.NO.getCode())
                .eq(BiliUserChatDetail::getIsGroup, IsFlagEnum.NO.getCode())
                .ne(BiliUserChatDetail::getDetailStatus, MessageStatus.RECALL.getCode())  //不要撤回
                .orderByDesc(BiliUserChatDetail::getTime)
                .last("limit 20"));
        // 将查询结果按时间升序排序
        List<BiliUserChatDetail> sortedDetails = details.stream()
                .sorted(Comparator.comparing(BiliUserChatDetail::getTime))
                .collect(Collectors.toList());
        return sortedDetails;
    }
    /**
     * 聊天登录
     */
    @Override
    public ResponseResult<?> login(ChatLoginDto chatLoginDto) {
        if (chatLoginDto.getCmd() == null || chatLoginDto.getAccessToken() == null || chatLoginDto.getUserId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        return null;
    }
}
